home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / PlainSocketImpl.java < prev    next >
Text File  |  1998-09-22  |  13KB  |  438 lines

  1. /*
  2.  * @(#)PlainSocketImpl.java    1.26 98/07/01
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  * 
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.net;
  16.  
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.io.InterruptedIOException;
  21. import java.io.FileDescriptor;
  22. import java.io.ByteArrayOutputStream;
  23.  
  24. /**
  25.  * Default Socket Implementation. This implementation does
  26.  * not implement any security checks.  It does support SOCKS version 4.
  27.  * Note this class should <b>NOT</b> be public.
  28.  *
  29.  * @author  Steven B. Byrne
  30.  * @version 1.26, 07/01/98
  31.  */
  32. class PlainSocketImpl extends SocketImpl
  33. {
  34.     /* instance variable for SO_TIMEOUT */
  35.     int timeout;   // timeout in millisec
  36.  
  37.     /* SOCKS related constants */
  38.  
  39.     private static final int SOCKS_PROTO_VERS        = 4;
  40.     private static final int SOCKS_REPLY_VERS        = 4;
  41.  
  42.     private static final int COMMAND_CONNECT        = 1;
  43.     private static final int COMMAND_BIND        = 2;
  44.  
  45.     private static final int REQUEST_GRANTED        = 90;
  46.     private static final int REQUEST_REJECTED        = 91;
  47.     private static final int REQUEST_REJECTED_NO_IDENTD  = 92;
  48.     private static final int REQUEST_REJECTED_DIFF_IDENTS = 93;
  49.  
  50.     public static final String socksServerProp        = "socksProxyHost";
  51.     public static final String socksPortProp        = "socksProxyPort";
  52.  
  53.     public static final String socksDefaultPortStr    = "1080";
  54.  
  55.     /**
  56.      * Load net library into runtime.
  57.      */
  58.     static {
  59.     System.loadLibrary("net");
  60.     initProto();
  61.     }
  62.  
  63.     /**
  64.      * Creates a socket with a boolean that specifies whether this
  65.      * is a stream socket (true) or an unconnected UDP socket (false).
  66.      */
  67.     protected synchronized void create(boolean stream) throws IOException {
  68.     fd = new FileDescriptor();
  69.     socketCreate(stream);
  70.     }
  71.  
  72.     /** 
  73.      * Creates a socket and connects it to the specified port on
  74.      * the specified host.
  75.      * @param host the specified host
  76.      * @param port the specified port 
  77.      */
  78.     protected void connect(String host, int port)
  79.         throws UnknownHostException, IOException
  80.     {
  81.     IOException pending = null;
  82.     try {
  83.         InetAddress address = InetAddress.getByName(host);
  84.  
  85.         try {
  86.         connectToAddress(address, port);
  87.         return;
  88.         } catch (IOException e) {
  89.         pending = e;
  90.         }
  91.     } catch (UnknownHostException e) {
  92.         pending = e;
  93.     }
  94.  
  95.     // everything failed
  96.     close();
  97.     throw pending;
  98.     }
  99.  
  100.     /** 
  101.      * Creates a socket and connects it to the specified address on
  102.      * the specified port.
  103.      * @param address the address
  104.      * @param port the specified port
  105.      */
  106.     protected void connect(InetAddress address, int port) throws IOException {
  107.     this.port = port;
  108.     this.address = address;
  109.  
  110.     try {
  111.         connectToAddress(address, port);
  112.         return;
  113.     } catch (IOException e) {
  114.         // everything failed
  115.         close();
  116.         throw e;
  117.     }
  118.     }
  119.  
  120.     private void connectToAddress(InetAddress address, int port) throws IOException {
  121.     if (usingSocks()) {
  122.         doSOCKSConnect(address, port);
  123.     } else {
  124.         doConnect(address, port);
  125.     }
  126.     }
  127.  
  128.     public void setOption(int opt, Object val) throws SocketException {
  129.     boolean on = true;
  130.     switch (opt) {
  131.         /* check type safety b4 going native.  These should never
  132.          * fail, since only java.Socket* has access to 
  133.          * PlainSocketImpl.setOption().
  134.          */
  135.         case SO_LINGER:
  136.         if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
  137.             throw new SocketException("Bad parameter for option");
  138.         if (val instanceof Boolean) { 
  139.             /* true only if disabling - enabling should be Integer */
  140.             on = false;
  141.         }
  142.         break;
  143.         case SO_TIMEOUT:
  144.         if (val == null || (!(val instanceof Integer)))
  145.             throw new SocketException("Bad parameter for SO_TIMEOUT");
  146.         int tmp = ((Integer) val).intValue();
  147.         if (tmp < 0)
  148.             throw new IllegalArgumentException("timeout < 0");
  149.         timeout = tmp;
  150.         return;
  151.         case SO_BINDADDR:
  152.         throw new SocketException("Cannot re-bind socket");
  153.         case TCP_NODELAY:
  154.         if (val == null || !(val instanceof Boolean))
  155.             throw new SocketException("bad parameter for TCP_NODELAY");
  156.         on = ((Boolean)val).booleanValue();
  157.         break;
  158.         default:
  159.         throw new SocketException("unrecognized TCP option: " + opt);
  160.     }
  161.     socketSetOption(opt, on, val);
  162.     }
  163.  
  164.     public Object getOption(int opt) throws SocketException {
  165.     if (opt == SO_TIMEOUT) {
  166.         return new Integer(timeout);
  167.     }
  168.     int ret = socketGetOption(opt);
  169.     /*
  170.      * The native socketGetOption() knows about 3 options.
  171.      * The 32 bit value it returns will be interpreted according
  172.      * to what we're asking.  A return of -1 means it understands
  173.      * the option but its turned off.  It will raise a SocketException
  174.      * if "opt" isn't one it understands.
  175.      */
  176.  
  177.     switch (opt) {
  178.     case TCP_NODELAY:
  179.         return (ret == -1) ? new Boolean(false): new Boolean(true);
  180.     case SO_LINGER:
  181.         return (ret == -1) ? new Boolean(false): (Object)(new Integer(ret));
  182.     case SO_BINDADDR:
  183.         InetAddress in = new InetAddress();
  184.         in.address = ret;
  185.         return in;
  186.     }
  187.     // should never get here
  188.     return null;
  189.     }
  190.  
  191.     /**
  192.      * Connect to the SOCKS server using the SOCKS connection protocol.
  193.      */
  194.     private void doSOCKSConnect(InetAddress address, int port) throws IOException {
  195.     connectToSocksServer();
  196.  
  197.     sendSOCKSCommandPacket(COMMAND_CONNECT, address, port);
  198.  
  199.     int protoStatus = getSOCKSReply();
  200.  
  201.     switch (protoStatus) {
  202.       case REQUEST_GRANTED:
  203.         // connection set up, return control to the socket client
  204.         return;
  205.  
  206.       case REQUEST_REJECTED:
  207.       case REQUEST_REJECTED_NO_IDENTD:
  208.         throw new SocketException("SOCKS server cannot conect to identd");
  209.  
  210.       case REQUEST_REJECTED_DIFF_IDENTS:
  211.         throw new SocketException("User name does not match identd name");
  212.     }
  213.     }
  214.     
  215.  
  216.     /**
  217.      * Read the response from the socks server.  Return the result code.
  218.      */
  219.     private int getSOCKSReply() throws IOException {
  220.     InputStream in = getInputStream();
  221.  
  222.     // REMIND: this could deal with reading < 8 bytes and buffering
  223.     // them up.
  224.  
  225.     byte response[] = new byte[8];
  226.  
  227.     int code;
  228.     if ((code = in.read(response)) != response.length) {
  229.         throw new SocketException("Malformed reply from SOCKS server");
  230.     }
  231.  
  232.     if (response[0] != 0) { // should be version 0
  233.         throw new SocketException("Malformed reply from SOCKS server");
  234.     }
  235.  
  236.     return response[1];    // the response code
  237.     }
  238.  
  239.     /**
  240.      * Just set up a connection to the SOCKS server and return.  The caller
  241.      * needs to handle the SOCKS initiation protocol with the server after
  242.      * the connection is established.
  243.      */
  244.     private void connectToSocksServer() throws IOException {
  245.  
  246.     String socksServerString = System.getProperty(socksServerProp);
  247.     if (socksServerString == null) {
  248.         // REMIND: this is too trusting of its (internal) callers --
  249.         // needs to robustly assert that SOCKS are in fact being used,
  250.         // and signal an error (in some manner) if SOCKS are not being
  251.         // used.
  252.         return;
  253.     }
  254.  
  255.     InetAddress socksServer = InetAddress.getByName(socksServerString);
  256.  
  257.     String socksPortString = System.getProperty(socksPortProp,
  258.                             socksDefaultPortStr);
  259.  
  260.     int socksServerPort;
  261.     try {
  262.         socksServerPort = Integer.parseInt(socksPortString);
  263.     } catch (Exception e) {
  264.         throw new SocketException("Bad port number format");
  265.     }
  266.     
  267.     doConnect(socksServer, socksServerPort);
  268.     }
  269.  
  270.  
  271.     /**
  272.      * The workhorse of the connection operation.  Tries several times to
  273.      * establish a connection to the given <host, port>.  If unsuccessful,
  274.      * throws an IOException indicating what went wrong.
  275.      */
  276.  
  277.     private void doConnect(InetAddress address, int port) throws IOException {
  278.     IOException pending = null;
  279.  
  280.     for (int i = 0 ; i < 3 ; i++) {
  281.         try {
  282.         socketConnect(address, port);
  283.         return;
  284.         } catch (ProtocolException e) {
  285.         // Try again in case of a protocol exception
  286.         close();
  287.         fd = new FileDescriptor();
  288.         socketCreate(true);
  289.         pending = e;
  290.         } catch (IOException e) {
  291.         // Let someone else deal with this exception
  292.         close();
  293.         throw e;
  294.         }
  295.     }
  296.  
  297.     // failed to connect -- tell our client the bad news
  298.     close();
  299.     throw pending;
  300.     }
  301.  
  302.  
  303.     /**
  304.      * Just creates and sends out to the connected socket a SOCKS command
  305.      * packet.
  306.      */
  307.     private void sendSOCKSCommandPacket(int command, InetAddress address,
  308.                     int port) throws IOException {
  309.     
  310.         byte commandPacket[] = makeCommandPacket(command, address, port);
  311.     OutputStream out = getOutputStream();
  312.  
  313.     out.write(commandPacket);
  314.     }
  315.  
  316.     /**
  317.      * Create and return a SOCKS V4 command packet.
  318.      */
  319.     private byte[] makeCommandPacket(int command, InetAddress address,
  320.                     int port) { 
  321.  
  322.     // base packet size = 8, + 1 null byte 
  323.     ByteArrayOutputStream byteStream = new ByteArrayOutputStream(8 + 1);
  324.  
  325.     byteStream.write(SOCKS_PROTO_VERS);
  326.     byteStream.write(command);
  327.  
  328.  
  329.     byteStream.write((port >> 8) & 0xff);
  330.     byteStream.write((port >> 0) & 0xff);
  331.  
  332.     byte addressBytes[] = address.getAddress();
  333.     byteStream.write(addressBytes, 0, addressBytes.length);
  334.  
  335.     String userName = System.getProperty("user.name");
  336.     byte userNameBytes[] = new byte[userName.length()];
  337.     userName.getBytes(0, userName.length(), userNameBytes, 0);
  338.  
  339.     byteStream.write(userNameBytes, 0, userNameBytes.length);
  340.     byteStream.write(0);    // null termination for user name
  341.  
  342.     return byteStream.toByteArray();
  343.     }
  344.  
  345.     /**
  346.      * Returns true if implementation should use the SOCKS protocol
  347.      * (i.e. the user has set the required properties to enable SOCKS to
  348.      * be used).
  349.      */
  350.     private boolean usingSocks() {
  351.     return (System.getProperty(socksServerProp) != null);
  352.     }
  353.     
  354.  
  355.     /**
  356.      * Binds the socket to the specified address of the specified local port.
  357.      * @param address the address
  358.      * @param port the port
  359.      */
  360.     protected synchronized void bind(InetAddress address, int lport) 
  361.     throws IOException
  362.     {
  363.     socketBind(address, lport);
  364.     }
  365.  
  366.     /**
  367.      * Listens, for a specified amount of time, for connections.
  368.      * @param count the amount of time to listen for connections
  369.      */
  370.     protected synchronized void listen(int count) throws IOException {
  371.     socketListen(count);
  372.     }
  373.  
  374.     /**
  375.      * Accepts connections.
  376.      * @param s the connection
  377.      */
  378.     protected synchronized void accept(SocketImpl s) throws IOException {
  379.     socketAccept(s);
  380.     }
  381.  
  382.     /**
  383.      * Gets an InputStream for this socket.
  384.      */
  385.     protected synchronized InputStream getInputStream() throws IOException {
  386.     return new SocketInputStream(this);
  387.     }
  388.  
  389.     /**
  390.      * Gets an OutputStream for this socket.
  391.      */
  392.     protected synchronized OutputStream getOutputStream() throws IOException {
  393.     return new SocketOutputStream(this);
  394.     }
  395.  
  396.     /**
  397.      * Returns the number of bytes that can be read without blocking.
  398.      */
  399.     protected synchronized int available() throws IOException {
  400.     return socketAvailable();
  401.     }
  402.  
  403.     /**
  404.      * Closes the socket.
  405.      */
  406.     protected void close() throws IOException {
  407.     if (fd != null) {
  408.         socketClose();
  409.         fd = null;
  410.     }
  411.     }
  412.  
  413.     /**
  414.      * Cleans up if the user forgets to close it.
  415.      */
  416.     protected void finalize() throws IOException {
  417.     close();
  418.     }
  419.  
  420.     private native void socketCreate(boolean isServer) throws IOException;
  421.     private native void socketConnect(InetAddress address, int port)
  422.     throws IOException;
  423.     private native void socketBind(InetAddress address, int port)
  424.     throws IOException;
  425.     private native void socketListen(int count)
  426.     throws IOException;
  427.     private native void socketAccept(SocketImpl s)
  428.     throws IOException;
  429.     private native int socketAvailable()
  430.     throws IOException;
  431.     private native void socketClose()
  432.     throws IOException;
  433.     private static native void initProto();
  434.     private native void socketSetOption(int cmd, boolean on, Object value) 
  435.     throws SocketException;
  436.     private native int socketGetOption(int opt) throws SocketException;
  437. }
  438.